package com.ruoyi.flowable.service.impl;


import cn.hutool.core.bean.BeanUtil;
import com.aspose.words.Document;
import com.aspose.words.PdfSaveOptions;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.xwpf.NiceXWPFDocument;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.flowable.domain.AppVersion;
import com.ruoyi.flowable.domain.ProcInstVo;
import com.ruoyi.flowable.domain.TaskVo;
import com.ruoyi.flowable.domain.bo.FormDataBo;
import com.ruoyi.flowable.domain.vo.BpmnConfigVo;
import com.ruoyi.flowable.domain.vo.FormDataVo;
import com.ruoyi.flowable.mapper.AppVersionMapper;
import com.ruoyi.flowable.service.IBpmnConfigService;
import com.ruoyi.flowable.service.IFormDataService;
import com.ruoyi.flowable.service.IProcInstService;
import com.ruoyi.flowable.utils.FlowableUtil;
import com.ruoyi.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class ProcInstServiceImpl implements IProcInstService {

    @Resource
    protected RuntimeService runtimeService;
    @Resource
    protected TaskService taskService;
    @Resource
    protected HistoryService historyService;

    @Resource
    private AppVersionMapper appVersionMapper;

    @Resource
    private IFormDataService formDataService;

    @Resource
    private IBpmnConfigService bpmnConfigService;
    @Resource
    private FlowableUtil flowableUtil;
    @Resource
    private ISysUserService sysUserService;


    @Override
    @Transactional
    public ProcessInstance startProcessInst(Long appVersionId, Map<String, Object> form) throws JsonProcessingException {
        AppVersion appVersion = appVersionMapper.selectById(appVersionId);
        if (appVersion == null) {
            throw new ServiceException("事项版本不存在或被删除");
        }
        ProcessDefinition processDefinition = flowableUtil.getDefinById(appVersion.getDefId());
        if (processDefinition == null || processDefinition.isSuspended()) {
            throw new ServiceException("流程定义不存在或被挂起");
        }

        Map<String, Object> variables = new HashMap<>();
        for (String field : form.keySet()) {
            if (appVersion.getBpmnXml().contains(field)) {
                variables.put(field, form.get(field));
            }
        }
        BpmnConfigVo bpmnConfig = bpmnConfigService.selectBpmnConfigByAppVersionId(appVersionId);
        variables.put("rejectNode", bpmnConfig == null ? "" : bpmnConfig.getRejectNode());

        FormDataBo formData = new FormDataBo();
        formData.setAppVersionId(appVersionId);
        formData.setFormVersionId(appVersion.getFormVersionId());
        formData.setFormId(appVersion.getFormId());
        formData.setSubmitData(JsonUtils.toJsonString(form));
        try {
            LoginUser sysuser = LoginHelper.getLoginUser();
            Authentication.setAuthenticatedUserId(sysuser.getUsername());//设置发起人
            variables.put("starter", sysuser.getUsername());
            variables.put("starterDeptId", sysuser.getDeptId());

            ProcessInstance procInst =  runtimeService.createProcessInstanceBuilder()
                .processDefinitionId(appVersion.getDefId()).businessKey(appVersion.getAppId().toString())
                .variables(variables).name(processDefinition.getName()).start();

            formData.setUserName(sysuser.getUsername());
            formData.setProcInstId(procInst.getProcessInstanceId());
            formDataService.insertByBo(formData);
            formDataService.asyncSaveData(formData); //落地表
            return procInst;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("流程启动错误");
        }
    }

    @Override
    public TableDataInfo myProcInstList(Integer pageNum, Integer pageSize, String processName) {

        TableDataInfo rspData = new TableDataInfo();
        String userName = LoginHelper.getUsername();
        HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery().startedBy(userName);
        if (processName != null && !processName.isEmpty()) {
            instanceQuery.processInstanceNameLike(processName);
        }
        instanceQuery.orderByProcessInstanceStartTime().desc();
        List<HistoricProcessInstance> insts = instanceQuery.listPage(pageSize * (pageNum - 1), pageSize);
        List<ProcInstVo> list = new ArrayList<>();
        for (HistoricProcessInstance p : insts) {
            ProcInstVo procInstVo = BeanUtil.toBean(p, ProcInstVo.class);
            if (p.getDurationInMillis() != null) procInstVo.setDuration(DateUtils.getDate(p.getDurationInMillis()));
            if (p.getBusinessStatus() == null) procInstVo.setBusinessStatus("进行中");
            list.add(procInstVo);
        }
        rspData.setRows(list);
        rspData.setTotal(instanceQuery.count());
        return rspData;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String procInstId, String deleteReason) {

        HistoricProcessInstance historicProcessInstance =
            historyService.createHistoricProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        if (historicProcessInstance == null) {
            throw new ServiceException("流程实例不存在: " + procInstId);
        }
        if (historicProcessInstance.getEndTime() == null) {
            // 删除流程实例（只执行这个是软删除）
            runtimeService.deleteProcessInstance(procInstId, deleteReason);
        }
        // 删除历史流程实例 (物理删除)
        historyService.deleteHistoricProcessInstance(procInstId);

        FormDataVo formDataVo = formDataService.selectFormDataByInstId(procInstId);
        if (formDataVo != null) {
            List<Long> ids = new ArrayList<>();
            ids.add(formDataVo.getDataId());
            try{
                formDataService.deleteWithValidByIds(ids, false);
                formDataService.deleteMapData("form_" + formDataVo.getFormId(), formDataVo.getDataId());
            }catch (Exception e){
                log.error(e.getMessage());
            }
        }
    }


    @Override
    public void exportPdf(String procInstId, HttpServletResponse response) throws Exception {

        FormDataVo formData = formDataService.selectFormDataByInstId(procInstId);
        String tableName = "form_" + formData.getFormId();
        Map<String, Object> dataInfo = formDataService.getDataInfo(tableName, formData.getDataId());
        if (dataInfo == null) throw  new ServiceException("数据还未归档，请稍后");



        //查询所有审批记录放入taskRecord
        List<TaskVo> taskRecord = new ArrayList<>();
        List<HistoricActivityInstance> hisActInsts = historyService.createNativeHistoricActivityInstanceQuery()
            .sql("SELECT * FROM ACT_HI_ACTINST WHERE PROC_INST_ID_ = #{procInstId} ORDER BY ID_ ASC ")
            .parameter("procInstId", procInstId).list();
        for (HistoricActivityInstance histInst : hisActInsts) {
            if ("userTask".equals(histInst.getActivityType()) && null != histInst.getEndTime()){
                TaskVo taskVo = new TaskVo();
                taskVo.setTaskName(histInst.getActivityName());
                taskVo.setEndTime(histInst.getEndTime());
                if (StringUtils.isNotBlank(histInst.getAssignee())) {
                    SysUser sysUser = sysUserService.selectUserByUserName(histInst.getAssignee());
                    if (sysUser != null){
                        taskVo.setAssigneeName(sysUser.getNickName());
                        taskVo.setAssigneeDeptName(sysUser.getDept().getDeptName());
                    }
                }
                List<Comment> commentList = taskService.getTaskComments(histInst.getTaskId());
                commentList.forEach(comment -> {
                    if (histInst.getTaskId().equals(comment.getTaskId())) {
                        taskVo.setComment(comment.getFullMessage());
                    }
                });
                taskRecord.add(taskVo);
            }
        }
        dataInfo.put("taskRecord", taskRecord);

        //循环表格需要这个
        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
        ConfigureBuilder configureBuilder = Configure.builder().bind("taskRecord",policy);
        //todo 可能需要重复表
        Configure config = configureBuilder.build();
        String  baseDir = "/www/wwwroot";

        //写入word模板
        String template = baseDir + "/wordTemplate/word_" + formData.getFormId() + ".docx";
        File file = new File(template);
        if (!file.exists()){
            throw new ServiceException("未设置模板");
        }

        NiceXWPFDocument newDoc = XWPFTemplate.compile(template, config).render(dataInfo).getXWPFDocument();
        String wordFile = baseDir + "/genWord/" + formData.getFormId() + "_" +System.currentTimeMillis() + ".docx";
        newDoc.write(Files.newOutputStream(Paths.get(wordFile)));
//        newDoc.write(response.getOutputStream());
        newDoc.close();

        //aspose docx2pdf
        Document wordDoc = new Document(wordFile);
        PdfSaveOptions pso = new PdfSaveOptions();
        wordDoc.save(response.getOutputStream(), pso);
    }

}
